package com.linskating.wheat.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.linskating.wheat.common.ErrorCode;
import com.linskating.wheat.constant.RedisLockConstant;
import com.linskating.wheat.constant.UserConstant;
import com.linskating.wheat.exception.BusinessException;
import com.linskating.wheat.mapper.TeamMapper;
import com.linskating.wheat.model.domain.Team;
import com.linskating.wheat.model.domain.User;
import com.linskating.wheat.model.domain.UserTeam;
import com.linskating.wheat.model.dto.TeamQuery;
import com.linskating.wheat.model.enums.TeamStatusEnum;
import com.linskating.wheat.model.request.teamRequest.TeamDismissRequest;
import com.linskating.wheat.model.request.teamRequest.TeamJoinRequest;
import com.linskating.wheat.model.request.teamRequest.TeamQuitRequest;
import com.linskating.wheat.model.request.teamRequest.TeamUpdateRequest;
import com.linskating.wheat.model.vo.TeamUserVO;
import com.linskating.wheat.model.vo.UserVO;
import com.linskating.wheat.service.TeamService;
import com.linskating.wheat.service.UserService;
import com.linskating.wheat.service.UserTeamService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;


/**
 * @author LinSk
 * @description 针对表【team(队伍)】的数据库操作Service实现
 * @createDate 2025-01-15 11:44:41
 */
@Service
@Slf4j
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
        implements TeamService, UserConstant, RedisLockConstant {

    private static final String SALT = "LinSkating";

    @Resource
    private UserTeamService userTeamService;

    @Resource
    private UserService userService;

    @Resource
    private RedissonClient redissonClient;

    @Override
    @Transactional(rollbackFor = BusinessException.class)
    public long creatTeam(Team team, User loginUser) {
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        int teamUsersNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if (teamUsersNum <= 0 || teamUsersNum > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "设置队伍的人数不符合规范");
        }
        String teamName = team.getName();
        if (StringUtils.isBlank(teamName) || teamName.length() > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "设置队伍的名字不符合规范");
        }
        String description = team.getDescription();
        if (StringUtils.isNotBlank(description) && description.length() > 512) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "设置队伍的描述不符合规范");
        }
        Integer status = team.getStatus();
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getTeamStatusEnum(status);
        teamStatusEnum = Optional.ofNullable(teamStatusEnum).orElse(TeamStatusEnum.PUBLIC);
        status = teamStatusEnum.getValue();

        String password = team.getPassword();
        if (status == TeamStatusEnum.SECRET.getValue()) {
            if (StringUtils.isBlank(password) || password.length() > 32) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "设置私密队伍的密码不符合规范");
            }
            password = DigestUtils.md5DigestAsHex((SALT + password).getBytes(StandardCharsets.UTF_8));
        }

        Date expireTime = team.getExpireTime();
        if (expireTime.before(new Date())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "设置队伍的超时不符合规范");
        }

        // 得到创建队伍的用户id
        Long userId = team.getUserId();
        // 判断userTeam关系表中存在多少该用户id
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        long userCreatTeamNums = this.count(queryWrapper);
        if (userCreatTeamNums >= 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍的数量超出限制");
        }

        team.setStatus(status);
        team.setId(null);
        team.setUserId(loginUser.getId());
        team.setPassword(password);
        boolean result = this.save(team);
        if (!result) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }

        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(loginUser.getId());
        userTeam.setTeamId(team.getId());
        userTeam.setJoinTime(new Date());
        result = userTeamService.save(userTeam);
        if (!result) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "插入用户和队伍信息到用户队伍表失败");
        }
        return team.getId();
    }

    @Override
    public List<TeamUserVO> listTeam(TeamQuery teamQuery, boolean isNotAdmit) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        if (teamQuery != null) {

            Long id = teamQuery.getTeamId();
            if (id != null && id > 0) {
                queryWrapper.eq("id", id);
            }

            List<Long> idList = teamQuery.getIdList();
            if (!CollectionUtils.isEmpty(idList)) {
                queryWrapper.in("id", idList);
            }

            Long userId = teamQuery.getUserId();
            if (userId != null && userId > 0) {
                queryWrapper.eq("userId", userId);
            }

            String searchText = teamQuery.getSearchText();
            if (StringUtils.isNotBlank(searchText)) {
                queryWrapper.and(qw -> qw.like("name", searchText).or().like("description", searchText));
            }

            String description = teamQuery.getDescription();
            if (StringUtils.isNotBlank(description)) {
                queryWrapper.eq("description", description);
            }

            String name = teamQuery.getName();
            if (StringUtils.isNotBlank(name)) {
                queryWrapper.eq("name", name);
            }

            Integer maxNum = teamQuery.getMaxNum();
            if (maxNum != null && maxNum > 0) {
                queryWrapper.eq("maxNum", maxNum);
            }

            Integer status = teamQuery.getStatus();
            TeamStatusEnum teamStatusEnum = TeamStatusEnum.getTeamStatusEnum(status);
            if (isNotAdmit && TeamStatusEnum.PRIVATE.equals(teamStatusEnum)) {
                throw new BusinessException(ErrorCode.NOT_AUTH);
            }
            if (teamStatusEnum != null) {
                queryWrapper.eq("status", teamStatusEnum.getValue());
            } else {
                queryWrapper.and(qw -> qw.eq("status", TeamStatusEnum.PUBLIC.getValue())
                        .or().eq("status", TeamStatusEnum.SECRET.getValue())
                        .or().eq("status", TeamStatusEnum.PRIVATE.getValue()));
            }
        }
        if (teamQuery != null && teamQuery.getIsExpireTimeActive()) {
            queryWrapper.and(qw -> qw.gt("expireTime", new Date()).or().isNull("expireTime"));
        }
        List<Team> teamList = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        mapToTeamUserVO(teamList, teamUserVOList);
        return teamUserVOList;
    }

    @Override
    public boolean updateTeam(TeamUpdateRequest updateRequest, User currentUser) {
        if (updateRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long teamId = updateRequest.getId();
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team oldTeam = this.getById(teamId);
        if (oldTeam == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }
        if (!oldTeam.getUserId().equals(currentUser.getId()) && userService.isNotAdmit(currentUser)) {
            throw new BusinessException(ErrorCode.NOT_AUTH);
        }
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getTeamStatusEnum(updateRequest.getStatus());
        if (TeamStatusEnum.SECRET.equals(teamStatusEnum)) {
            if (StringUtils.isBlank(updateRequest.getPassword())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密房间必须设置密码");
            }
        } else {
            updateRequest.setPassword(null);
        }
        Team newTeam = new Team();
        BeanUtils.copyProperties(updateRequest, newTeam);
        return this.updateById(newTeam);
    }

    @Override
    public boolean joinTeam(TeamJoinRequest teamJoinRequest, User currentUser) {
        if (teamJoinRequest == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        Long teamId = teamJoinRequest.getId();
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        Team team = this.getById(teamId);
        Date expireTime = team.getExpireTime();
        if (expireTime != null && new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已过期");
        }
        Integer teamStatus = team.getStatus();
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getTeamStatusEnum(teamStatus);
        if (TeamStatusEnum.PRIVATE.equals(teamStatusEnum)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "不可加入私密队伍");
        }
        // 判断加密之后的密码是否匹配
        String teamPassword = team.getPassword();
        String joinRequestPassword = teamJoinRequest.getPassword();
        joinRequestPassword = DigestUtils.md5DigestAsHex((SALT + joinRequestPassword).getBytes(StandardCharsets.UTF_8));
        if (TeamStatusEnum.SECRET.equals(teamStatusEnum)) {
            if (StringUtils.isBlank(joinRequestPassword) || !joinRequestPassword.equals(teamPassword)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
            }
        }

        RLock lock = redissonClient.getLock(JOIN_LOCK);
        try {
            while (true) {
                if (lock.tryLock(0, -1, TimeUnit.SECONDS)) {
                    QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
                    Long userId = currentUser.getId();
                    queryWrapper.eq("userId", userId);
                    long count = userTeamService.count(queryWrapper);
                    if (count >= 5) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "加入队伍数量超过5个");
                    }

                    queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("teamId", teamId);
                    count = userTeamService.count(queryWrapper);
                    if (count >= team.getMaxNum()) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍数量已满");
                    }

                    queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("teamId", teamId).eq("userId", userId);
                    count = userTeamService.count(queryWrapper);
                    if (count > 0) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "不可重复加入队伍");
                    }
                    UserTeam userTeam = new UserTeam();
                    userTeam.setTeamId(teamId);
                    userTeam.setUserId(userId);
                    userTeam.setJoinTime(new Date());
                    return userTeamService.save(userTeam);
                }
            }
        } catch (InterruptedException e) {
            log.error("Join Team error", e);
            return false;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, User currentUser) {
        if (teamQuitRequest == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        long teamId = teamQuitRequest.getTeamId();
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        Long userId = currentUser.getId();
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId).eq("userId", userId);
        long count = userTeamService.count(queryWrapper);
        if (count != 1) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "未加入队伍");
        }
        Long leaderUserId = team.getUserId();
        // 队长退出队伍
        if (userId.equals(leaderUserId)) {
            queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("teamId", teamId).last("order by id asc limit 2");
            List<UserTeam> userTeamList = userTeamService.list(queryWrapper);
            if (userTeamList.isEmpty()) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
            // 如果队伍只有一个人(那这个人就是队长) 就解散
            if (userTeamList.size() == 1) {
                this.removeById(teamId);
            } else {
                // 获得队长名额的用户（第二顺位的用户）
                UserTeam nextTeamLeader = userTeamList.get(1);
                // 将队伍信息中队长的id改为第二顺位的用户
                team.setUserId(nextTeamLeader.getUserId());
                this.updateById(team);
            }
            // 删除 队长 在 队伍用户表里的关系
            queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("teamId", teamId).eq("userId", leaderUserId);
            boolean result = userTeamService.remove(queryWrapper);
            if (!result) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
        } else {
            queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("teamId", teamId).eq("userId", userId);
            boolean result = userTeamService.remove(queryWrapper);
            if (!result) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean dismissTeam(TeamDismissRequest teamDismissRequest, User currentUser) {
        if (teamDismissRequest == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        long teamId = teamDismissRequest.getTeamId();
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        Long userId = currentUser.getId();
        Long teamLeaderId = team.getUserId();
        int userRole = currentUser.getUserRole();

        // 队长或管理员都可以解散队伍
        if (!userId.equals(teamLeaderId) || ADMIN_ROLE != userRole) {
            throw new BusinessException(ErrorCode.NOT_AUTH);
        }

        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", team.getId());
        boolean result = userTeamService.remove(queryWrapper);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        result = this.removeById(team);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        return true;
    }

    @Override
    public boolean deleteTeam(long teamId, boolean notAdmit) {
        if (notAdmit) {
            throw new BusinessException(ErrorCode.NOT_AUTH, "管理员才能删除队伍");
        }
        boolean result = this.removeById(teamId);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId);
        result = userTeamService.remove(queryWrapper);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        return true;
    }

    @Override
    public Page<TeamUserVO> getTeamPage(int thePageNum, int pageSize, Long userId) {
        if (thePageNum <= 0 || pageSize <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        if (userId == null || userId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId).select("teamId");
        List<UserTeam> teamList = userTeamService.list(queryWrapper);
        List<Long> teamIdList = teamList.stream().map(UserTeam::getTeamId).toList();
        QueryWrapper<Team> teamQueryWrapper = new QueryWrapper<>();
        teamQueryWrapper.ne("id", teamIdList)
                .ne("status", TeamStatusEnum.PRIVATE.getValue())
                .ge("expireTime", new Date());
        Page<Team> teamPage = new Page<>(thePageNum, pageSize);
        teamPage = this.page(teamPage, teamQueryWrapper);
        List<Team> teamPageRecords = teamPage.getRecords();
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        mapToTeamUserVO(teamPageRecords, teamUserVOList);
        Page<TeamUserVO> teamUserVOPage = new Page<>(thePageNum, pageSize, teamUserVOList.size());
        teamUserVOPage.setRecords(teamUserVOList);
        return teamUserVOPage;
    }

    @Override
    public Long totalCount(Long userId) {
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        List<Long> userTeamList = userTeamService.list(queryWrapper).stream().map(UserTeam::getTeamId).toList();
        QueryWrapper<Team> teamQueryWrapper = new QueryWrapper<>();
        teamQueryWrapper.ne("id", userTeamList)
                .ne("status", TeamStatusEnum.PRIVATE.getValue())
                .ge("expireTime", new Date());
        return this.count(teamQueryWrapper);
    }

    private void mapToTeamUserVO(List<Team> source, List<TeamUserVO> target) {
        for (Team team : source) {
            Long id = team.getUserId();
            if (id == null || id <= 0) continue;
            User user = userService.getById(id);
            TeamUserVO teamUserVO = new TeamUserVO();
            if (user != null) {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                teamUserVO.setCreatUser(userVO);
            }
            BeanUtils.copyProperties(team, teamUserVO);
            teamUserVO.setCurNum((int) searchUserCountInTeam(team.getId()));
            target.add(teamUserVO);
        }
    }

    private long searchUserCountInTeam(Long teamId) {
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId);
        return userTeamService.count(queryWrapper);
    }
}
